<?php
namespace EApiSwoole\Container;
class Container implements \ArrayAccess
{
    protected static $instance;
    protected $bindings = [];
    protected $instances = [];
    public function __construct()
    {
    }
    public function bind($abstract, $concrete = null, $shared = false)
    {
        $this->bindings[$abstract]['concrete'] = $concrete;
        $this->bindings[$abstract]['shared'] = $shared;
    }
    public function singleton($abstract, $concrete = null, $shared = true)
    {
        $this->bind($abstract, $concrete, $shared);
    }
    public function make($abstract, $parameters = [])
    {
        return $this->resolve($abstract, $parameters);
    }
    public function resolve($abstract, $parameters = [])
    {
        if (isset($this->instances[$abstract])) {
            return $this->instances[$abstract];
        }
        // 从容器中获取
        $object = $this->getConrete($abstract);
        // 判断是否为闭包
        if ($object instanceof \Closure) {
            // 执行闭包返回结果
            return $object();
        }

        // 判断是否为一个object对象
        if (!is_object($object)) {
            // 创建对象并且return
//            $object = new $object(...$parameters);
            //添加反射对象
            try {
                $reflector = new \ReflectionClass($object);
            } catch (\Exception $e) {
                throw new \Exception($e->getMessage());
            }
            if (! $reflector->isInstantiable()) {
               return "不能示例化对象";
            }
            $constructor = $reflector->getConstructor();
            if (is_null($constructor)) {
                return new $object();
            }
            $dependencies = $constructor->getParameters();
            try {
                $instances = $this->getDependencies($dependencies);
            } catch (\Exception $e) {
                throw new \Exception($e->getMessage());
            }
            $object= $reflector->newInstanceArgs($instances);
        }

        // 是否采用单例
        if ($this->has($abstract) && $this->bindings[$abstract]['shared']) {
            $this->instances[$abstract] = $object;
        }

        return $object;
    }
     public function getDependencies($parameters)
    {
        $dependencies = [];

        foreach($parameters as $parameter) {
            $dependency = $parameter->getClass();
            if (is_null($dependency)) {
                if($parameter->isDefaultValueAvailable()) {
                    $dependencies[] = $parameter->getDefaultValue();
                } else {
                    //不是可选参数的为了简单直接赋值为字符串0
                    $dependencies[] = '0';
                }
            } else {
                //递归解析出依赖类的对象
                $dependencies[] = $this->make($parameter->getClass()->name);
            }
        }

        return $dependencies;
    }
    public function getConrete($abstract)
    {
        if ($this->has($abstract)) {
            $abstract = $this->bindings[$abstract]['concrete'];
        }
        return $abstract;
    }

    public function has($abstract)
    {
        return isset($this->bindings[$abstract]['concrete']) || isset($this->instances[$abstract]);
    }

    public function getBindings()
    {
        return $this->bindings;
    }
    public function instance($abstract, $instance)
    {
        // 1. 从容器中移除
        $this->removeBindings($abstract);
        $this->instances[$abstract] = $instance;
    }
    public function removeBindings($abstract)
    {
        if (!isset($this->bindings[$abstract])) {
            return;
        }
        unset($this->bindings[$abstract]);
    }

    public static function setInstance($container = null)
    {
        return static::$instance = $container;
    }
    public static function getInstance()
    {
        if (is_null(static::$instance)) {
            static::$instance = new static;
        }
        return static::$instance;
    }
    /**
     * @inheritDoc
     */
    public function offsetExists($offset)
    {
        // TODO: Implement offsetExists() method.
    }

    /**
     * @inheritDoc
     */
    public function offsetGet($offset)
    {
        return $this->make($offset);
    }

    /**
     * @inheritDoc
     */
    public function offsetSet($offset, $value)
    {
        // TODO: Implement offsetSet() method.
    }

    /**
     * @inheritDoc
     */
    public function offsetUnset($offset)
    {
        // TODO: Implement offsetUnset() method.
    }
}